﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OWLServer.owl.tipos;

namespace OWLServer.owl.utilidades
{
    /// <summary>
    /// Clase que contiene métodos para manejar los problemas generados en una ontología.
    /// </summary>
    class UtilidadesProblemas
    {
        /// <summary>
        /// Comprueba posibles problemas que pueda tener el individuo que le pasemos.
        /// </summary>
        /// <param name="ontologia">Ontología a evaluar los problemas</param>
        public static void comprobarProblemas(Ontologia ontologia)
        {
            List<Problema> problemas = new List<Problema>();

            // Problemas generados por los individuos
            foreach (Individuo individuo in ontologia.Individuos)
            {
                problemas.AddRange(comprobarProblemasClasesDisjuntas(individuo));
                problemas.AddRange(comprobarProblemasUnionClasesDisjuntas(individuo));
                problemas.AddRange(comprobarProblemasIndividuoEquivalentesDiferentes(individuo));
                problemas.AddRange(comprobarProblemasPropiedadesInversas(individuo));
            }

            // Problemas generados por los propiedades de dato
            foreach (Propiedad propiedad in ontologia.PropiedadesDatos)
            {
                if (!UtilidadesPropiedades.isPropiedadTop(propiedad, ontologia.getEspaciosNombresDictiorary()))
                {
                    problemas.AddRange(comprobarProblemasPropiedadesSubPropiedades(propiedad, ontologia));
                }
            }

            // Problemas generados por los propiedades de objeto
            foreach (Propiedad propiedad in ontologia.PropiedadesObjetos)
            {
                if (!UtilidadesPropiedades.isPropiedadTop(propiedad, ontologia.getEspaciosNombresDictiorary()))
                {
                    problemas.AddRange(comprobarProblemasPropiedadesSubPropiedades(propiedad, ontologia));
                }
            }

            ontologia.Problemas = problemas;
        }

        /// <summary>
        /// Comprueba posibles problemas que pueda tener el individuo que le pasemos con respecto a sus clases por ser disjuntas.
        /// </summary>
        /// <param name="individuo">Individuo a evaluar</param>
        /// <returns>Lista de problemas detectados</returns>
        private static List<Problema> comprobarProblemasClasesDisjuntas(Individuo individuo)
        {
            List<Problema> problemas = new List<Problema>();
            List<Clase> clasesBase;
            List<Clase> disjuntas;

            // Miramos si hay algun problema con las clases disjuntas
            IList<Clase> clases = UtilidadesClases.obtenerPadresYEquivalentes(individuo.Tipos);
            foreach (Clase clase in clases)
            {
                clasesBase = new List<Clase>();
                disjuntas = new List<Clase>();
                clasesBase.Add(clase);
                // Obtenemos las disjuntas a la clase
                UtilidadesClases.obtenerClasesDisjuntas(clasesBase, disjuntas);

                foreach (Clase disjunta in disjuntas)
                {
                    if (clase.Equals(disjunta))
                    {
                        problemas.Add(new Problema(individuo, TipoProblema.CLASE_DISJUNTA, clase, disjunta));
                    }
                }
            }

            return problemas;
        }

        /// <summary>
        /// Comprueba posibles problemas que pueda tener el individuo que le pasemos con respecto a sus uniones de clases disjuntas.
        /// </summary>
        /// <param name="individuo">Individuo a evaluar</param>
        /// <returns>Lista de problemas detectados</returns>
        private static List<Problema> comprobarProblemasUnionClasesDisjuntas(Individuo individuo)
        {
            List<Problema> problemas = new List<Problema>();
            int nDisjuntas;
            IList<Clase> elementosUnion;

            // Miramos si hay algun problema las uniones de clases disjuntas
            IList<Clase> clases = UtilidadesClases.obtenerPadresYEquivalentes(individuo.Tipos);            
            foreach (Clase clase in clases)
            {
                foreach (UnionClasesDisjuntas union in clase.UnionesClasesDisjuntas)
                {
                    nDisjuntas = clase.UnionesClasesDisjuntas.Count;
                    foreach (Clase claseUnion in union.ElementosUnionClasesDisjuntas)
                    {
                        elementosUnion = UtilidadesClases.obtenerPadresYEquivalentes(claseUnion);
                        foreach (Clase elementoUnion in elementosUnion)
                        {
                            foreach (Clase clase2 in clases)
                            {
                                if (clase2.Equals(elementoUnion))
                                {
                                    nDisjuntas--;
                                }
                            }
                        }
                    }
                    if (nDisjuntas <= 0)
                    {
                        problemas.Add(new Problema(individuo, TipoProblema.UNION_CLASES_DISJUNTAS, clase, union));
                    }
                }
            }

            return problemas;
        }

        /// <summary>
        /// Comprueba posibles problemas que pueda tener el individuo porque sea equivalente y diferente a otro, al mismo tiempo.
        /// </summary>
        /// <param name="individuo">Individuo a evaluar</param>
        /// <returns>Lista de problemas detectados</returns>
        private static List<Problema> comprobarProblemasIndividuoEquivalentesDiferentes(Individuo individuo)
        {
            List<Problema> problemas = new List<Problema>();

            // Miramos si hay algun problema
            foreach (Individuo igual in individuo.Iguales)
            {
                if (individuo.Diferentes.Contains(igual))
                {
                    problemas.Add(new Problema(TipoProblema.INDIVIDUO_IGUAL_Y_DIFERENTE, individuo, igual));
                }
            }

            return problemas;
        }

        /// <summary>
        /// Conprueba que las subpropiedades de la propiedad tienen el mismo dominio y rango que la propiedad.
        /// </summary>
        /// <param name="propiedad">Propiedad a evaluar</param>
        /// <returns>Lista de problemas detectados</returns>
        /// <param name="ontologia">Ontologia</param>
        private static List<Problema> comprobarProblemasPropiedadesSubPropiedades(Propiedad propiedad, Ontologia ontologia)
        {
            List<Problema> problemas = new List<Problema>();

            foreach (Propiedad padrePropiedad in propiedad.PadrePropiedades)
            {
                if (!UtilidadesPropiedades.isPropiedadTop(padrePropiedad, ontologia.getEspaciosNombresDictiorary()))
                {
                    // Miramos si los dominios son iguales
                    if (!UtilidadesClases.sonIguales(propiedad.Dominios, padrePropiedad.Dominios))
                    {
                        problemas.Add(new Problema(TipoProblema.PROPIEDAD_SUB_PROPIEDAD, propiedad, padrePropiedad));
                    }

                    // Miramos si los rangos son iguales
                    if (!UtilidadesClases.sonIguales(propiedad.Rangos, padrePropiedad.Rangos))
                    {
                        problemas.Add(new Problema(TipoProblema.PROPIEDAD_SUB_PROPIEDAD, propiedad, padrePropiedad));
                    }
                }
            }

            return problemas;
        }

        /// <summary>
        /// Comprueba posibles problemas que puedan existir, entre individuos, porque las propiedades sean inversas. P1(X,Y) iff P2(Y,X)
        /// </summary>
        /// <param name="individuo">Individuo a evaluar</param>
        /// <returns>Lista de problemas detectados</returns>
        private static List<Problema> comprobarProblemasPropiedadesInversas(Individuo individuo)
        {
            List<Problema> problemas = new List<Problema>();

            foreach (PropiedadObjetoIndividuo propiedad1IndividuoX in individuo.PropiedadesObjetos)
            {
                if (propiedad1IndividuoX.PropiedadObjeto.InversosPropiedadesObjeto.Count > 0)
                {
                    // Recorremos las propiedades del individuo Y y vemos si es inversa con la propiedad1
                    foreach (PropiedadObjetoIndividuo propiedad2IndividuoY in propiedad1IndividuoX.Individuo.PropiedadesObjetos)
                    {
                        if (propiedad1IndividuoX.PropiedadObjeto.InversosPropiedadesObjeto.Contains(propiedad2IndividuoY.PropiedadObjeto))
                        {
                            // Si cumple la propiedad inversa
                            if (individuo.Equals(propiedad2IndividuoY.Individuo))
                            {
                                // Recorremos los problemas de antes por si hubiera habido alguno que hubieramos generado de este tipo
                                for (int i = problemas.Count - 1; i >= 0; i--)
                                {
                                    if (problemas[i].ElementoImplicado1.Equals(propiedad1IndividuoX.PropiedadObjeto) && problemas[i].ElementoImplicado2.Equals(propiedad2IndividuoY.PropiedadObjeto)
                                        && problemas[i].Individuo1.Equals(individuo) && problemas[i].ElementoImplicado2.Equals(propiedad1IndividuoX.Individuo))
                                    {
                                        // La eliminamos porque ya se está cumpliendo
                                        problemas.RemoveAt(i);
                                    }
                                }
                            }
                            else
                            {
                                problemas.Add(new Problema(individuo, propiedad1IndividuoX.Individuo, TipoProblema.PROPIEDAD_INVERSA, propiedad1IndividuoX.PropiedadObjeto, propiedad2IndividuoY.PropiedadObjeto));
                            }
                        }
                    }
                }
            }

            return problemas;
        }
    }
}
